<template>
  <div>
    <!-- <h1>角色管理</h1> -->
    <el-card>
      <el-form inline class="inlineForm" style="height: 90px;">
        <el-form-item label="职位搜索">
          <el-input placeholder="请输入需要搜索的职位" v-model="keywordThatUsedToSearchPosition"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" size="default" :disabled="keywordThatUsedToSearchPosition ? false : true"
            @click="handlerForUserClickSearchButton">搜索</el-button>
          <el-button type="normal" size="default" @click="handlerForUserClickResetButton">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>
    <el-card style="margin: 10px 0px;">
      <el-button type="primary" size="default" icon="Plus"
        @click="handlerForUserClickAddPositionButton" v-haveThisButton="`btn.Role.add`">添加职位</el-button>
      <el-table style="margin: 10px 0px;" border :data="positionList">
        <el-table-column align="center" type="index" label="序号" width="60px"></el-table-column>
        <el-table-column align="center" label="ID" prop="id"></el-table-column>
        <el-table-column align="center" label="职位名称" prop="roleName" show-overflow-tooltip></el-table-column>
        <el-table-column align="center" label="创建时间" prop="createTime" show-overflow-tooltip></el-table-column>
        <el-table-column align="center" label="更新时间" prop="updateTime" show-overflow-tooltip></el-table-column>
        <el-table-column align="center" label="操作" width="280px">
          <!-- 注：“row”就是“存储了职位信息的对象”↓ -->
          <template #default="{ row, $index }">
            <el-button type="primary" size="small" icon="User"
              @click="handlerForUserClickAssignPermissionsButton(row)" v-haveThisButton="`btn.Role.assgin`">分配权限</el-button>
            <el-button type="primary" size="small" icon="Edit"
              @click="handlerForUserClickEditButton(row)" v-haveThisButton="`btn.Role.update`">编辑</el-button>
              <el-popconfirm width="260px" :title="`你确定要删除${row.roleName}吗?`" @confirm="handlerForUserClickConfirmButtonInTheElPopConfirm(row.id)">
                <template #reference>
                  <el-button type="danger" size="small" icon="Delete" v-haveThisButton="`btn.Role.remove`">删除</el-button>                  
                </template>
              </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页器↓ -->
      <el-pagination @current-change="getPositionInformation" @size-change="handlerForChangeThePageSize"
        v-model:current-page="currentPageNumber" v-model:page-size="currentPageSize" :page-sizes="[10, 20, 30, 401]"
        :background="true" layout="prev, pager, next, jumper,->,sizes,total" :total="totalNumberOfPositions" />
    </el-card>
    <!-- 添加与修改职位时需要用到的对话框组件↓ -->
    <el-dialog v-model="isThedialogIncludeFormVisible"
      :title="anObjectThatStoresPositionInformation.id ? '更新职位' : '添加职位'" width="500">
      <el-form :model="anObjectThatStoresPositionInformation" :rules="rules" ref="refForForm">
        <el-form-item label="职位名称" prop="roleName">
          <el-input v-model="anObjectThatStoresPositionInformation.roleName" placeholder="请您输入职位名称" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handlerForUserClickTheConfirmButton">确定</el-button>
          <el-button @click="handlerForUserClickTheCancelButton">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 为某个职位分配权限时需要用到的抽屉组件↓ -->
    <el-drawer v-model="isThedrawerVisible" direction="rtl">
      <template #header>
        <h4>分配菜单与按钮的权限</h4>
      </template>
      <template #default>
        <!-- 树形控件（属性解释：“data”：需要展示的数据；“node-key”：每个树节点用来作为唯一标识的属性；“show-checkbox”:节点是否可被选择；“default-checked-keys”：默认勾选的节点的 key 的数组；“default-expanded-keys”：默认展开的节点的 key 的数组；“default-expand-all”：是否默认展开所有节点）↓ -->
        <el-tree ref="refForElTreeComponent" style="max-width: 600px" :data="menuPermissionList" show-checkbox
          node-key="id" :default-checked-keys="idListForLevel4NodesThatBeSelected" :props="defaultProps"
          default-expand-all />
      </template>
      <template #footer>
        <div style="flex: auto">
          <el-button @click="isThedrawerVisible = false">取消</el-button>
          <el-button type="primary" @click="handlerForUserClickTheConfirmButtonInTheDrawer">确定</el-button>
        </div>
      </template>
    </el-drawer>
  </div>
</template>

<script lang="ts" setup name="Role">
import { ref, onMounted, reactive, nextTick } from 'vue'
import { requestGetPositionList, requestAddOrUpdateAPosition, requestGetAllPermissionOfMenuAndButton, requestAssignPermissionsForAPosition,requestDeleteAPosition } from '@/api/acl/role'
import type {
  typeOfResponseDataOfTheApiThatGetPositionList,
  typeOfPropertyNamedRecordsInTheResponseDataOfTheApiThatGetPositionList,
  typeOfObjectThatRepresentsAPosition,
  typeOfResponseDataOfTheApiThatGetAllPermissionOfMenuAndButton,
  typeOfMenuAndButtonPermissionList,
  typeOfObjectThatStoreInformationOfMenuPermissionOrButtonPermission,
} from '@/api/acl/role/type'
import { ElMessage } from 'element-plus'
import useLayoutSettingStore from '@/store/modules/setting'
let refForElTreeComponent = ref<any>() // 获取树形控件的实例对象
let currentPageNumber = ref<number>(1)
let currentPageSize = ref<number>(10)
let layoutSettingStore = useLayoutSettingStore()
let keywordThatUsedToSearchPosition = ref<string>('') // 定义一个变量，它被用于存储“用户搜索职位时输入的关键字”
let positionList = <typeOfPropertyNamedRecordsInTheResponseDataOfTheApiThatGetPositionList>ref([])
let totalNumberOfPositions = ref<number>(0) // 定义一个变量，它被用于存储“职位信息列表的数据条目总数”
let isThedialogIncludeFormVisible = ref<boolean>(false)
let anObjectThatStoresPositionInformation = reactive<typeOfObjectThatRepresentsAPosition>({
  roleName: '',
}) // 定义一个对象，它被用于存储“用户输入的职位信息”
// 定义表单验证规则对象↓
let rules = {
  roleName: [
    { required: true, validator: validatorForRoleName, trigger: 'blur' },
  ],
}
let idListForLevel4NodesThatBeSelected = ref<number[]>([]) // 定义一个变量，它被用于存储“用户勾选的菜单或按钮权限列表中的四级节点（即按钮权限）的ID”
let refForForm = ref() // 获取表单组件的实例对象
let menuPermissionList = <typeOfMenuAndButtonPermissionList>ref([]) // 定义一个变量，它被用于存储某个用户拥有的“所有菜单与按钮的权限”
onMounted(() => {
  getPositionInformation()
})
let isThedrawerVisible = ref<boolean>(false) // 定义一个变量，它被用于控制抽屉组件的显示与隐藏
const defaultProps = { // 设置树形控件中的“标签”和“子节点”分别使用数据中的哪个属性↓
  children: 'children',
  label: 'name',
}
/**
 * @description 函数“validatorForRoleName”用于验证职位名称是否符合要求。如果用户输入的职位信息不符合要求，则会显示一条错误信息
 */
function validatorForRoleName(role: Object, value: string, callback: Function) {
  if (value.trim().length >= 2) {
    callback()
  } else {
    callback(new Error('职位名称的长度必须大于2个字符'))
  }
}
/**
 * @description 函数“getPositionInformation”用于获取职位信息。它通过调用“requestGetPositionList”函数，并传入当前页码和每页显示的数据量作为参数来获取职位信息列表。
 * @param pageNumber 页码
 * @param pageSize 每页显示多少条数据
 */
// 注：在 TypeScript 中，类型注解和参数默认值可以共存。共存时，如下面的代码所示，默认值需要被写在类型注解的后面。例如，为参数“pageSize”指定默认值“10”时，应该这样写：
async function getPositionInformation(pageNumber: number = 1, pageSize: number = 10) {
  currentPageNumber.value = pageNumber
  let result: typeOfResponseDataOfTheApiThatGetPositionList = await requestGetPositionList(pageNumber, pageSize, keywordThatUsedToSearchPosition.value)
  console.log(result) // {code: 200, message: '成功', data: {…}, ok: true}
  if (result.code == 200) {
    totalNumberOfPositions.value = result.data.total
    positionList.value = result.data.records
  } else {
    ElMessage({
      type: 'error',
      message: "获取职位信息失败",
    })
  }
}
/**
 * @description “handlerForChangeThePageSize”函数被用于处理“用户改变每页可显示的数据条目数”时发生的事件。当用户改变了每页可显示的数据条目数，此函数就会被调用
 * @param newPageSize 新的每页可显示的数据条目数
 */
function handlerForChangeThePageSize(newPageSize: number) {
  console.log(newPageSize)
  currentPageSize.value = newPageSize
  getPositionInformation(undefined, currentPageSize.value) // 第一个参数的位置留空，直接使用默认值“1”，第二个参数沿用“newPageSize”的值，使得改变“每页可显示的条目数”后虽然回到了第一页，但仍可显示相应数量的数据条目
}
/**
 * 函数“handlerForUserClickSearchButton”被用于处理“用户点击搜索按钮”时发生的事件。当用户点击了搜索按钮，此函数就会被调用

 */
function handlerForUserClickSearchButton() {
  getPositionInformation()
  keywordThatUsedToSearchPosition.value = ""
}
/**
 * 函数“handlerForUserClickResetButton”被用于处理“用户点击重置按钮”时发生的事件。当用户点击“重置”按钮时，此函数就会被调用
 */
function handlerForUserClickResetButton() {
  layoutSettingStore.isThePageBeRefreshed = !layoutSettingStore.isThePageBeRefreshed
}
/**
 * 函数“handlerForUserClickAddPositionButton”被用于处理“用户点击添加职位按钮”时发生的事件。当用户点击“添加职位”按钮时，此函数就会被调用
 */
function handlerForUserClickAddPositionButton() {
  isThedialogIncludeFormVisible.value = true // 显示对话框组件
  Object.assign(anObjectThatStoresPositionInformation, { roleName: '', id: 0 }) // 通过“合并对象”的方式来清空表单项“职位名称”的值，并清除“职位ID”（注：若不清除ID，则在用户先点击“编辑职位”按钮，再点击“添加职位”按钮时，由于职位ID未被清除，对话框顶部的标题内容仍会显示为“更新职位”）
  nextTick(() => {
    refForForm.value.clearValidate("roleName") // 调用表单组件的实例对象提供的“clearValidate”方法，清除上一次表单校验不符合校验规则时，针对“roleName”字段给出的提示信息
  })
}
/**
 * @description 函数“handlerForUserClickTheConfirmButton”被用于处理“用户点击确定按钮”时发生的事件
 */
async function handlerForUserClickTheConfirmButton() {
  await refForForm.value.validate() // 调用表单组件的实例对象提供的“validate”方法，保证所有表单项都通过了验证之后，才会执行后续的操作
  let result: typeOfResponseDataOfTheApiThatGetPositionList = await requestAddOrUpdateAPosition(anObjectThatStoresPositionInformation) // 调用“requestAddOrUpdateAPosition”函数，传入用户输入的职位信息对象作为参数，从而实现添加职位的功能
  console.log(result); // {code: 200, message: '成功', data: {…}, ok: true}
  if (result.code == 200) {
    ElMessage({
      type: 'success',
      message: anObjectThatStoresPositionInformation.id ? "更新职位成功" : "添加职位成功",
    })
    isThedialogIncludeFormVisible.value = false // 隐藏对话框组件
    getPositionInformation(anObjectThatStoresPositionInformation.id ? currentPageNumber.value : 1) // 重新获取职位信息，以便更新页面上显示的职位信息列表
  } else {
    ElMessage({
      type: 'error',
      message: "添加职位信息失败",
    })
  }
}
function handlerForUserClickTheCancelButton() {
  isThedialogIncludeFormVisible.value = false
}
/**
 * @description 函数“handlerForUserClickEditButton”被用于处理“用户点击编辑按钮”时发生的事件。当用户点击某个职位的“编辑”按钮时，此函数就会被调用
 */
function handlerForUserClickEditButton(row: typeOfObjectThatRepresentsAPosition) {
  isThedialogIncludeFormVisible.value = true
  console.log(row) // Proxy(Object) {id: 18450, createTime: '2025-03-19 12:44:49', updateTime: '2025-03-19 12:44:49', roleName: '123123123', remark: null}
  Object.assign(anObjectThatStoresPositionInformation, row) // 通过“合并对象”的方式，将用户点击的职位的信息复制到用于存储用户输入的职位信息对象的变量中
  nextTick(() => {
    refForForm.value.clearValidate("roleName")
  })
}
/**
 * @description 函数“handlerForUserClickAssignPermissionsButton”被用于处理“用户点击分配权限按钮”时发生的事件。当用户点击某个职位的“分配权限”按钮时，此函数就会被调用
 */
async function handlerForUserClickAssignPermissionsButton(row: typeOfObjectThatRepresentsAPosition) {
  isThedrawerVisible.value = true // 显示抽屉组件
  Object.assign(anObjectThatStoresPositionInformation, row) // 通过“合并对象”的方式，存储当前用户要分配权限的职位的信息
  let result: typeOfResponseDataOfTheApiThatGetAllPermissionOfMenuAndButton = await requestGetAllPermissionOfMenuAndButton((anObjectThatStoresPositionInformation.id as number))
  console.log(result) // {code: 200, message: '成功', data: Array(9), ok: true}
  if (result.code == 200) {
    menuPermissionList.value = result.data
    idListForLevel4NodesThatBeSelected.value = filterLevel4NodesThatBeSelected(menuPermissionList.value, []) // 过滤出所有四级节点（即按钮权限）的ID，并将其存储在变量“idListForLevel4NodesThatBeSelected”中
  } else {
    ElMessage({
      type: 'error',
      message: "获取职位信息失败",
    })

  }
}
/**
 * @description 函数“handlerForUserClickTheConfirmButtonInTheDrawer”被用于处理“用户点击位于抽屉组件中的确定按钮”时发生的事件。当用户点击位于抽屉组件中的“确定”按钮时，此函数就会被调用
 */
async function handlerForUserClickTheConfirmButtonInTheDrawer() {
  const positionId = (anObjectThatStoresPositionInformation.id as number) // 获取当前用户要分配权限的职位的ID
  let idListForLevel4NodesThatBeSelected = refForElTreeComponent.value.getCheckedKeys() // 获取所有被用户选中的节点的ID组成的数组（注：在“节点可被选中”的情况下，方法“getCheckedKeys”将返回由当前选中节点的key组成的数组，方法“getHalfCheckedKeys”则将返回由当前被半选中的节点的key组成的数组）
  let idListForHalfCheckedNodes = refForElTreeComponent.value.getHalfCheckedKeys()
  console.log(idListForLevel4NodesThatBeSelected) // (3) [11, 13, 14]
  console.log(idListForHalfCheckedNodes) // (3) [1, 7, 8]
  let permissionId = idListForLevel4NodesThatBeSelected.concat(idListForHalfCheckedNodes) // 使用数组提供的“concat”方法将所有被用户选中的节点的ID组成的数组和所有被半选中的节点的ID组成的数组合并为一个新的数组
  let result = await requestAssignPermissionsForAPosition(positionId, permissionId) // 调用“requestAssignPermissionsForAPosition”函数，传入职位ID和所有被用户选中的节点的ID组成的数组作为参数，从而实现为某个职位分配权限的功能
  console.log(result) // {code: 200, message: '成功', data: null, ok: true}
  if (result.code == 200) {
    isThedrawerVisible.value = false // 隐藏抽屉组件
    ElMessage({
      type: 'success',
      message: "分配权限成功",
    })
    // getPositionInformation(currentPageNumber.value) // 重新获取职位信息，以便更新页面上显示的职位信息列表
    // 注：为了防止出现“用户为自己修改权限后，页面上仍保留该用户已经无法访问的功能模块入口”的情况，此处不能只重新获取职位信息列表，而是应该直接刷新当前页面，让用户回到登录页面，重新登录，以获取到最新的用户信息↕
    window.location.reload()
  } else {
    ElMessage({
      type: 'error',
      message: "分配权限失败",
    })

  }
}
/**
 * @description 函数“filterLevel4NodesThatBeSelected”被用于过滤出所有被用户选中的四级节点（即按钮权限）的ID，并将其存储在变量“idListForLevel4NodesThatBeSelected”中
 */
function filterLevel4NodesThatBeSelected(permissionList: typeOfMenuAndButtonPermissionList, initDataForPermissionList: any = []) {
  console.log(permissionList) // Proxy(Array) {0: {…}, 1: {…}, 2: {…}, 3: {…}, 4: {…}, 5: {…}, 6: {…}, 7: {…}, 8: {…}}
  permissionList.forEach((item: typeOfObjectThatStoreInformationOfMenuPermissionOrButtonPermission) => {
    if (item.select && item.level == 4) {
      initDataForPermissionList.push(item.id)
      if (item.children && item.children.length > 0) { // 此处之所以要保证子节点数组不为空，是因为在某些情况下，上级节点可能没有子节点（即这个用户不具备某些菜单权限），这种情况下就不需要递归调用此函数）
        filterLevel4NodesThatBeSelected(item.children, initDataForPermissionList) // 递归调用，以便过滤出所有被用户选中的四级节点（即按钮权限）的ID，并将其存储在变量“idListForLevel4NodesThatBeSelected”中
      }
    }
  });
  return initDataForPermissionList
}
/**
 * @description 函数“handlerForUserClickConfirmButtonInTheElPopConfirm”被用于处理“用户点击位于ElPopConfirm组件中的确定按钮”时发生的事件

 * @param positionId 职位ID

 */
async function handlerForUserClickConfirmButtonInTheElPopConfirm(positionId: number) {
  let result:any = await requestDeleteAPosition(positionId) // 调用“requestDeleteAPosition”函数，传入职位ID作为参数，从而实现删除职位的功能
  console.log(result) // {code: 200, message: '成功', data: null, ok: true}
  if (result.code == 200) {
    ElMessage({
      type: 'success',
      message: "删除职位成功",
    })
    getPositionInformation(positionList.value.length>1?currentPageNumber.value:currentPageNumber.value-1) // 重新获取职位信息，以便更新页面上显示的职位信息列表
  } else {
    ElMessage({
      type: 'error',
      message: "删除职位失败",
    })
    
  }
}
</script>

<style scoped>
.inlineForm {
  width: 100%;
  height: 50px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>